package com.sharp.core.utils;

import com.sharp.core.math.AABB;
import com.sharp.core.math.UnVec3;
import com.sharp.core.math.Vec3;
import com.sharp.core.raytrace.Ray;
import com.sharp.core.voxel.Voxel;

public class LineClip {

	// Cohen-Sutherland segments
	public enum Segment {
		LEFT(0x01), RIGHT(0x02), TOP(0x04), BOTTOM(0x08), NEAR(0x10), FAR(0x20);
		
		private int code;
		
		private Segment(int bit) {
			this.code = bit;
		}
		
		public int getCode() {
			return code;
		}
		
		public boolean isCode(int bit) {
			return (code & bit) != 0;
		}
		
		public static boolean isIn(int bit0, int bit1) {
			return (bit0 & bit1) != 0;
		}
		
		public int or(Segment s) {
			return code | s.code;
		}
	}
	
	public static boolean CohenSutherland3D(AABB cube, Vec3 from, Vec3 to) {
		int start = calcCode(cube, from);
		int end = calcCode(cube, to);
		
		if ( (start | end) == 0 )						// Mindkettő végpont a voxelben van
			return false;
		else if ( (start & end) != 0 )					// Mindkettő külön van
			return false;
		return true;
	}
	
	public static Voxel.Side CohenSutherland3DSide(AABB cube, float size, Vec3 from, Vec3 to) {
		int start = calcCode(cube, from);
		int end = calcCode(cube, to);
		
		if ( (start | end) == 0 )						// Mindkettő végpont a voxelben van
			return null;
		else if ( (start & end) != 0 )					// Mindkettő külön van
			return null;
		if ( ((start & Segment.BOTTOM.getCode())!=0) || ((start&Segment.TOP.getCode())!=0) )
			return Voxel.Side.TOP;
		else if ( ((start & Segment.NEAR.getCode())!=0) || ((start&Segment.FAR.getCode())!=0) )
			return Voxel.Side.FRONT;
		else if ( ((start & Segment.LEFT.getCode())!=0) || ((start&Segment.RIGHT.getCode())!=0) )
			return Voxel.Side.LEFT;
		return null;
	}
	
	public static float RayAABB(AABB cube, Ray ray) {
		UnVec3 from = ray.getOrigin();
		UnVec3 to = new Vec3(ray.getDir()).multiply(1000).add(ray.getOrigin());
		int start = calcCode(cube, from);
		int end = calcCode(cube, to);
		
		if ( (start | end) == 0 )						// Mindkettő végpont a voxelben van
			return -1;
		else if ( (start & end) != 0 )					// Mindkettő külön van
			return -1;
		
		float x, y, z;
		float X = to.getX() - from.getX();
		float Y = to.getY() - from.getY();
		float Z = to.getZ() - from.getZ();
		float maxX = cube.getMax().getX();
		float minX = cube.getMin().getX();
		float maxY = cube.getMax().getY();
		float minY = cube.getMin().getY();
		float maxZ = cube.getMax().getZ();
		float minZ = cube.getMin().getZ();
		
		float t = 0;
		
		if ( Segment.RIGHT.isCode(start) ) {
			x = maxX - from.getX();
			//t = X / x;
			t = x / ray.getDir().getX();
		} else if ( Segment.LEFT.isCode(start) ) {
			x = minX - from.getX();
			//t = X / x;
			t = x / ray.getDir().getX();
		}
		if ( Segment.TOP.isCode(start) ) {
			y = maxY - from.getY();
			//t = Y / y;
			t = Y / ray.getDir().getY();
		} else if ( Segment.BOTTOM.isCode(start) ) {
			y = minY - from.getY();
			//t = Y / y;
			t = y / ray.getDir().getY();
		}
		if ( Segment.NEAR.isCode(start) ) {
			z = minZ - from.getZ();
			//t = Z / z;
			t = z / ray.getDir().getZ();
		} else if ( Segment.FAR.isCode(start) ) {
			z = maxZ - from.getZ();
			//t = Z / z;
			t = z / ray.getDir().getZ();
		}
		
		return t;
	}

	private static int calcCode(AABB cube, UnVec3 linePos) {
		int code = 0;
		if ( linePos.getX() < cube.getMin().getX() )
			code |= Segment.LEFT.getCode();
		else if ( linePos.getX() > cube.getMax().getX() )
			code |= Segment.RIGHT.getCode();
		
		if ( linePos.getY() < cube.getMin().getY() )
			code |= Segment.BOTTOM.getCode();
		else if ( linePos.getY() > cube.getMax().getY() )
			code |= Segment.TOP.getCode();
		
		if ( linePos.getZ() < cube.getMin().getZ())
			code |= Segment.NEAR.getCode();
		else if ( linePos.getZ() > cube.getMax().getZ() )
			code |= Segment.FAR.getCode();
		
		return code;
	}
	
}
